/*
 * Copyright 2011 Guidebee Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 */
//--------------------------------- PACKAGE ------------------------------------
package com.mapdigit.practices.compositeui.collections;

//--------------------------------- IMPORTS ------------------------------------
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import com.google.inject.Injector;
import com.mapdigit.practices.compositeui.WorkItem;

//[------------------------------ MAIN CLASS ----------------------------------]
////////////////////////////////////////////////////////////////////////////////
//--------------------------------- REVISIONS ----------------------------------
// Date       Name                 Tracking #         Description
// --------   -------------------  -------------      --------------------------
// 13AUG2011  James Shen                 	      Code review
////////////////////////////////////////////////////////////////////////////////
/**
 * Represents a collection of objects, used by the WorkItem>
 * <P>
 * <hr>
 * <hr><b>&copy; Copyright 2011 Guidebee Pty Ltd. All Rights Reserved.</b>
 * @version     1.00, 13/08/11
 * @author      Guidebee Pty Ltd.
 */
public class ManagedObjectCollection<TItem> {

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Initializes an instance of the ManagedObjectCollection<TItem> class.
     * @param injector injector object
     * @param workItem work item object
     * @param parentCollection parent collection.
     */
    public ManagedObjectCollection(Injector injector, WorkItem workItem,
            ManagedObjectCollection<TItem> parentCollection) {
        this.parentCollection = parentCollection;
        this.injector = injector;
        this.workItem = workItem;
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Gets an enumerator which returns all the objects in the container along 
     * with it's id.
     * @return all item in the container
     */
  
    public Set<Entry<String,TItem>> getSetEntry(){
        return container.entrySet();
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Gets an enumerator which returns all the objects in the container
     * @return all item in the container
     */
    public Enumeration<TItem> getEmulatation(){
        return container.elements();
    }
    
    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Get item based on its type.
     * @param <TItem> class type
     * @param type type
     * @return object of given type.
     */
    public <TItem> TItem get(Class<TItem> type) {
        List<TItem> items = (List<TItem>) findByType(type);
        if (items != null && items.size() > 0) {
            return items.get(0);
        }
        return null;
    }
    
    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Get item based on it's id.
     * @param id id of the item.
     * @return item object.
     */
    public TItem getItem(String id) {

        TItem item = container.get(id);
        while (item == null && parentCollection != null) {
            Hashtable<String, TItem> parentContainer = parentCollection.container;
            item = parentContainer.get(id);
            parentCollection = parentCollection.parentCollection;
        }
        return item;
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * The count of the items in the collection.
     * @return count of the items
     */
    public int getCount() {
        return container.size();
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Add an item to the collection.
     * @param item item to add.
     */
    public void add(TItem item) {
        add(item, null);
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Add item to the collection with given id.
     * @param item item to add.
     * @param id id of the item.
     */
    public void add(TItem item, String id) {
        build(item.getClass(), id, item);
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Add a new type.
     * @param typeToBuild type to be added.
     */
    public void addNew(Class typeToBuild) {
        addNew(typeToBuild, null);
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Add a new type to the collection with given id.
     * @param typeToBuild new type.
     * @param id id of the type.
     */
    public void addNew(Class<? extends Object> typeToBuild, String id) {
        build(typeToBuild, id, null);
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * remove the item from the collection.
     * @param item to to be removed.
     */
    public void remove(TItem item) {
        String key = "";
        if (container.containsValue(item)) {
            for (Entry<String, TItem> entry : container.entrySet()) {
                if (entry.getValue() == item) {
                    key = entry.getKey();
                    break;
                }
            }
        }
        if (!"".equals(key)) {
            container.remove(key);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Determines if the collection contains an object with the given ID.
     * @param id The ID of the object
     * @return >Returns true if the collection contains the object; 
     *  false otherwise.
     */
    public boolean contains(String id){
        return getItem(id)!=null;
    }
    
    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 13AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Find all item with given type include parent's
     * @param searchType search type
     * @return a list of the items of given type.
     */
    public Collection<TItem> findByType(Class searchType) {
        List<TItem> result = new ArrayList<TItem>();
        for (TItem item : container.values()) {
            if (item.getClass() == searchType) {
                result.add(item);
            }
        }
        
        while (parentCollection != null) {
            Hashtable<String, TItem> parentContainer = parentCollection.container;
            for (TItem item : parentContainer.values()) {
                if (item.getClass() == searchType) {
                    result.add(item);
                }
            }
            parentCollection = parentCollection.parentCollection;
            parentContainer = parentCollection.container;
        }
        return result;
    }
    
    
    private ManagedObjectCollection<TItem> parentCollection;
    private WorkItem workItem;
    private final Hashtable<String, TItem> container = new Hashtable<String, TItem>();
    private Injector injector;

    private TItem build(Class typeToBuild, String id, Object item)
            throws IllegalArgumentException {
        if (typeToBuild != null && container.containsKey(id)) {
            throw new IllegalArgumentException("An object with this ID already exists:" + id);
        }
        if (item != null && item == workItem) {
            throw new IllegalArgumentException("A WorkItem cannot be added to itself." + id);
        }
        if (item == null) {
            item = injector.getInstance(typeToBuild);
        }
        if (!container.containsValue((TItem) item)) {
            container.put(id, (TItem) item);
        }
        return (TItem) item;
    }
}
